package com.app.bitmapmodulelearn.bussiness.compression

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Matrix
import android.view.View
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import kotlin.math.abs
import kotlin.math.sqrt


/**
 *  Create by zhouwd on 2020-01-17
 *  Describe:
 */

private const val STATUS_INIT = 1 //常量初始化

private const val STATUS_ZOOM_OUT = 2//图片放大状态常量
private const val STATUS_ZOOM_IN = 3//图片缩小状态常量
private const val STATUS_MOVE = 4//图片拖动状态常量
private const val STATUS_SITU = 5//图片原位

private val TAG = ZoomImageView::class.java.simpleName

class ZoomImageView : View {


    private val mMatrix = Matrix()
    private var sourceBitmap: Bitmap? = null//待展示的Bitmap对象
    private var currentStatus: Int = 0//记录当前操作的状态，可选值为STATUS_INIT、STATUS_ZOOM_OUT、STATUS_ZOOM_IN和STATUS_MOVE
    private var mWidth: Int = 0//ZoomImageView控件的宽度
    private var mHeight: Int = 0//ZoomImageView控件的高度
    private var centerPointX: Float = 0.0f//记录两指同时放在屏幕上时，中心点的横坐标值
    private var centerPointY: Float = 0.0f//记录两指同时放在屏幕上时，中心点的纵坐标值
    private var currentBitmapWidth: Float = 0.0f//记录当前图片的宽度，图片被缩放时，这个值会一起变动
    private var currentBitmapHeight: Float = 0.0f//记录当前图片的高度，图片被缩放时，这个值会一起变动
    private var lastXMove = -1f//记录上次手指移动时的横坐标
    private var lastYMove = -1f//记录上次手指移动时的纵坐标
    private var movedDistanceX: Float = 0.0f//记录手指在横坐标方向上的移动距离
    private var movedDistanceY: Float = 0.0f//记录手指在纵坐标方向上的移动距离
    private var totalTranslateX: Float = 0.0f//记录图片在矩阵上的横向偏移值
    private var totalTranslateY: Float = 0.0f//记录图片在矩阵上的纵向偏移值
    private var totalRatio: Float = 0.0f//记录图片在矩阵上的总缩放比例
    private var scaledRatio: Float = 0.0f//记录手指移动的距离所造成的缩放比例
    private var initRatio: Float = 0.0f//记录图片初始化时的缩放比例
    private var lastFingerDis: Double = 0.0//记录上次两指之间的距离


    /**
     * ZoomImageView构造函数，将当前操作状态设为STATUS_INIT。
     *
     * @param context
     * @param attrs
     */
    constructor(context: Context, attrs: AttributeSet): super(context, attrs){
        currentStatus = STATUS_INIT
    }


    /*将待展示的图片设置进来。
    * @param bitmap
    * 待展示的Bitmap对象
    */
    fun setImageBitmap(bitmap: Bitmap) {
        sourceBitmap = bitmap
        invalidate()
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int,
                          bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        if (changed) {
            // 分别获取到ZoomImageView的宽度和高度
            mWidth = width
            mHeight = height
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        Log.i(TAG, "onTouchEvent")
        if (initRatio == totalRatio) {
            parent.requestDisallowInterceptTouchEvent(false)
        } else {
            parent.requestDisallowInterceptTouchEvent(true)
        }

        when (event.actionMasked) {
            MotionEvent.ACTION_POINTER_DOWN -> if (event.pointerCount == 2) {
                // 当有两个手指按在屏幕上时，计算两指之间的距离
                lastFingerDis = distanceBetweenFingers(event)
                Log.i(TAG, "onTouchEvent ---> lastFingerDis --> $lastFingerDis")
            }
            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_MOVE -> if (event.pointerCount == 1) {
                // 只有单指按在屏幕上移动时，为拖动状态
                val xMove = event.x
                val yMove = event.y
                if (lastXMove == -1f && lastYMove == -1f) {
                    lastXMove = xMove
                    lastYMove = yMove
                }
                currentStatus = STATUS_MOVE
                movedDistanceX = xMove - lastXMove
                movedDistanceY = yMove - lastYMove
                // 进行边界检查，不允许将图片拖出边界
//                if (totalTranslateX + movedDistanceX > 0) {
//                    movedDistanceX = 0f
//                } else if (width - (totalTranslateX + movedDistanceX) > currentBitmapWidth) {
//                    movedDistanceX = 0f
//                }
//                if (totalTranslateY + movedDistanceY > 0) {
//                    movedDistanceY = 0f
//                } else if (height - (totalTranslateY + movedDistanceY) > currentBitmapHeight) {
//                    movedDistanceY = 0f
//                }
                // 调用onDraw()方法绘制图片
                invalidate()
                lastXMove = xMove
                lastYMove = yMove
            } else if (event.pointerCount == 2) {
                // 有两个手指按在屏幕上移动时，为缩放状态
                centerPointBetweenFingers(event)
                val fingerDis = distanceBetweenFingers(event)
                currentStatus = if (fingerDis > lastFingerDis) {
                    STATUS_ZOOM_OUT
                } else {
                    STATUS_ZOOM_IN
                }
                // 进行缩放倍数检查，最大只允许将图片放大6倍，最小可以缩小到初始化比例
                val minRatio = initRatio / 6
                val maxRatio = initRatio * 6
                if (currentStatus == STATUS_ZOOM_OUT && totalRatio < maxRatio || currentStatus == STATUS_ZOOM_IN && totalRatio > minRatio) {
                    scaledRatio = (fingerDis / lastFingerDis).toFloat()
                    totalRatio *= scaledRatio
                    if (totalRatio > maxRatio) {
                        totalRatio = maxRatio
                    } else if (totalRatio < minRatio) {
                        totalRatio = minRatio
                    }
                    // 调用onDraw()方法绘制图片
                    invalidate()
                    lastFingerDis = fingerDis
                }
            }
            MotionEvent.ACTION_POINTER_UP -> if (event.pointerCount == 2) {
                // 手指离开屏幕时将临时值还原
                lastXMove = -1f
                lastYMove = -1f
            }
            MotionEvent.ACTION_UP -> {
                // 手指离开屏幕时将临时值还原
                lastXMove = -1f
                lastYMove = -1f
                if (currentStatus == STATUS_MOVE) {
                    if (scaledRatio <= initRatio) {
                        currentStatus = STATUS_INIT
                        invalidate()
                    }
                }
            }
            else -> {
            }
        }
        return true
    }

    /*根据currentStatus的值来决定对图片进行什么样的绘制操作。*/
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        when (currentStatus) {
            STATUS_ZOOM_OUT, STATUS_ZOOM_IN -> zoom(canvas)
            STATUS_MOVE -> move(canvas)
            STATUS_INIT -> {
                initBitmap(canvas)
                if (sourceBitmap != null) {
                    sourceBitmap?.let {
                        canvas.drawBitmap(it, mMatrix, null)
                    }
                }
            }
            STATUS_SITU -> {
                sourceBitmap?.let {
                    mMatrix.reset()
                    mMatrix.postTranslate(0.0f, 0.0f)
                    canvas.drawBitmap(it, mMatrix, null)
                }
            }
            else -> if (sourceBitmap != null) {
                sourceBitmap?.let {
                    canvas.drawBitmap(it, mMatrix, null)
                }
            }

        }
    }


    /**
     * 对图片进行缩放处理。
     *
     * @param canvas
     */
    private fun zoom(canvas: Canvas) {

        sourceBitmap?.let {
            mMatrix.reset()
            // 将图片按总缩放比例进行缩放
            mMatrix.postScale(totalRatio, totalRatio)
            val scaledWidth = it.width * totalRatio
            val scaledHeight = it.height * totalRatio
            var translateX: Float
            var translateY: Float
            // 如果当前图片宽度小于屏幕宽度，则按屏幕中心的横坐标进行水平缩放。否则按两指的中心点的横坐标进行水平缩放
//            if (currentBitmapWidth < width) {
//                translateX = (width - scaledWidth) / 2f
//            } else {
//                translateX = totalTranslateX * scaledRatio + centerPointX * (1 - scaledRatio)
//                // 进行边界检查，保证图片缩放后在水平方向上不会偏移出屏幕
//                if (translateX > 0) {
//                    translateX = 0f
//                } else if (width - translateX > scaledWidth) {
//                    translateX = width - scaledWidth
//                }
//            }
            translateX = totalTranslateX * scaledRatio + centerPointX * (1 - scaledRatio)
            // 如果当前图片高度小于屏幕高度，则按屏幕中心的纵坐标进行垂直缩放。否则按两指的中心点的纵坐标进行垂直缩放
//            if (currentBitmapHeight < height) {
//                translateY = (height - scaledHeight) / 2f
//            } else {
//                translateY = totalTranslateY * scaledRatio + centerPointY * (1 - scaledRatio)
//                // 进行边界检查，保证图片缩放后在垂直方向上不会偏移出屏幕
//                if (translateY > 0) {
//                    translateY = 0f
//                } else if (height - translateY > scaledHeight) {
//                    translateY = height - scaledHeight
//                }
//            }
            translateY = totalTranslateY * scaledRatio + centerPointY * (1 - scaledRatio)
            // 缩放后对图片进行偏移，以保证缩放后中心点位置不变
            mMatrix.postTranslate(translateX, translateY)
            totalTranslateX = translateX
            totalTranslateY = translateY
            currentBitmapWidth = scaledWidth
            currentBitmapHeight = scaledHeight
            canvas.drawBitmap(it, mMatrix, null)
        }
    }


    /**
     * 对图片进行平移处理
     *
     * @param canvas
     */
    private fun move(canvas: Canvas) {

        sourceBitmap?.let {

            mMatrix.reset()
            // 根据手指移动的距离计算出总偏移值
            val translateX = totalTranslateX + movedDistanceX
            val translateY = totalTranslateY + movedDistanceY
            // 先按照已有的缩放比例对图片进行缩放
            mMatrix.postScale(totalRatio, totalRatio)
            // 再根据移动距离进行偏移
            mMatrix.postTranslate(translateX, translateY)
            totalTranslateX = translateX
            totalTranslateY = translateY

            canvas.drawBitmap(it, mMatrix, null)
        }

    }


    /**
     * 对图片进行初始化操作，包括让图片居中，以及当图片大于屏幕宽高时对图片进行压缩。
     *
     * @param canvas
     */
    private fun initBitmap(canvas: Canvas) {
        sourceBitmap?.let {
            mMatrix.reset()
            val bitmapWidth = it.width
            val bitmapHeight = it.height
            if (bitmapWidth > width || bitmapHeight > height) {
                if (bitmapWidth - width > bitmapHeight - height) {
                    // 当图片宽度大于屏幕宽度时，将图片等比例压缩，使它可以完全显示出来
                    val ratio = width / (bitmapWidth * 1.0f)
                    mMatrix.postScale(ratio, ratio)

                    val translateY = (height - bitmapHeight * ratio) / 2f
                    // 在纵坐标方向上进行偏移，以保证图片居中显示
                    mMatrix.postTranslate(0.0f, translateY)
                    totalTranslateX = 0.0f
                    totalTranslateY = translateY
                    initRatio = ratio
                    totalRatio = initRatio
                } else {
                    // 当图片高度大于屏幕高度时，将图片等比例压缩，使它可以完全显示出来
                    val ratio = height / (bitmapHeight * 1.0f)
                    mMatrix.postScale(ratio, ratio)
                    val translateX = (width - bitmapWidth * ratio) / 2f
                    // 在横坐标方向上进行偏移，以保证图片居中显示
                    mMatrix.postTranslate(translateX, 0.0f)
                    totalTranslateX = translateX
                    totalTranslateY = 0.0f
                    initRatio = ratio
                    totalRatio = initRatio
                }
                currentBitmapWidth = bitmapWidth * initRatio
                currentBitmapHeight = bitmapHeight * initRatio
            } else {
                // 当图片的宽高都小于屏幕宽高时，直接让图片居中显示
                val translateX = (width - it.width) / 2f
                val translateY = (height - it.height) / 2f
                mMatrix.postTranslate(translateX, translateY)
                totalTranslateX = translateX
                totalTranslateY = translateY
                initRatio = 1f
                totalRatio = initRatio
                currentBitmapWidth = bitmapWidth.toFloat()
                currentBitmapHeight = bitmapHeight.toFloat()
            }
            canvas.drawBitmap(it, mMatrix, null)
        }

    }


    /**
     * 计算两个手指之间的距离。
     *
     * @param event
     * @return 两个手指之间的距离
     */
    private fun distanceBetweenFingers(event: MotionEvent): Double {
        val disX = abs(event.getX(0) - event.getX(1))
        val disY = abs(event.getY(0) - event.getY(1))
        return sqrt((disX * disX + disY * disY).toDouble())
    }


    /**
     * 计算两个手指之间中心点的坐标。
     *
     * @param event
     */
    private fun centerPointBetweenFingers(event: MotionEvent) {
        val xPoint0 = event.getX(0)
        val yPoint0 = event.getY(0)
        val xPoint1 = event.getX(1)
        val yPoint1 = event.getY(1)
        centerPointX = (xPoint0 + xPoint1) / 2
        centerPointY = (yPoint0 + yPoint1) / 2
    }

}